"""
建筑项目进度调度系统（最终优化版）
核心功能：
1. 使用FLR+两位数字标准化命名所有楼层活动
2. 自动处理不同日期格式输入
3. 带缓存的关键路径计算
4. 完善的错误处理机制
"""

import datetime
from typing import List, Dict, Tuple
from criticalpath import Node
from osc03.domain.activities.AssemblingGrouted import AssemblingGrouted
from osc03.domain.activities.Grout import Grout
from osc03.domain.activities.StandardFloorErection import StandardFloorErection
from osc03.static.constants import Constants


class ProjectScheduler:
    """项目进度调度器（最终版）"""

    def __init__(self):
        """初始化调度器实例"""
        self.activity_sequence: List[StandardFloorErection] = []
        self.activity_registry: Dict[str, StandardFloorErection] = {}
        self.project_network: Node = None
        self.schedule_cache: Dict[str, Tuple] = {}

    def _generate_floor_name(self, floor_num: int) -> str:
        """生成标准楼层名称（FLR+两位数字）

        Args:
            floor_num: 实际楼层号（1-18）

        Returns:
            格式化后的楼层名称（如FLR01）

        Raises:
            ValueError: 当楼层号不在1-18范围内时
        """
        if not 1 <= floor_num <= 18:
            raise ValueError("楼层号必须介于1-18之间")
        return f"FLR{floor_num:02d}"

    def create_project_network(self) -> Node:
        """创建项目网络（惰性初始化）"""
        if self.project_network is None:
            self._create_construction_activities()
            self._build_activity_dependencies()
        return self.project_network

    def _create_construction_activities(self):
        """创建所有施工活动（三层结构）"""
        self.activity_sequence = []

        # 1. 首层（FLR01）
        self._add_floor(
            floor_num=1,
            duration=7,
            worker_skill='1',
            worker_num=5,
            crane_type='1'
        )

        # 2. 标准层（FLR02-FLR17）
        for floor_num in range(2, 2 + Constants.N_STD_FLR):
            self._add_floor(
                floor_num=floor_num,
                duration=6,
                worker_skill='1',
                worker_num=5,
                crane_type='1'
            )

        # 3. 顶层（FLR18）
        self._add_floor(
            floor_num=18,
            duration=7,
            worker_skill='1',
            worker_num=5,
            crane_type='1'
        )

    def _add_floor(self, floor_num: int, **kwargs):
        """添加单个楼层活动到序列"""
        floor = StandardFloorErection(
            name=self._generate_floor_name(floor_num),
            **kwargs
        )
        self.activity_sequence.append(floor)

    def _build_activity_dependencies(self):
        """构建活动间依赖关系网络"""
        self.project_network = Node('Construction_Project')

        # 注册所有活动
        for activity in self.activity_sequence:
            self.project_network.add(activity)
            self.activity_registry[activity.name] = activity

        # 线性依赖（连续楼层）
        for i in range(len(self.activity_sequence) - 1):
            self.project_network.link(
                self.activity_sequence[i],
                self.activity_sequence[i + 1]
            )

        # 特殊依赖（灌浆活动）
        self._link_special_dependencies()

    def _link_special_dependencies(self):
        """链接特殊依赖关系"""
        assemblings = [
            a for a in self.activity_sequence
            if isinstance(a, AssemblingGrouted)
        ]
        grouts = [
            g for g in self.activity_sequence
            if isinstance(g, Grout)
        ]

        for assembly in assemblings:
            for grout in grouts:
                self.project_network.link(assembly, grout)

    def get_schedule(self, start_date: str = '2023-03-10') -> Tuple[List, datetime.datetime]:
        """获取项目进度计划（主入口方法）

        Args:
            start_date: 支持格式：
                - '2023-03-10'（推荐）
                - '2023-3-10'（自动转换）

        Returns:
            (activities, start_date_obj)

        Raises:
            ValueError: 日期格式无效时
            RuntimeError: 网络计算失败时
        """
        # 标准化日期格式
        try:
            date_obj = datetime.datetime.strptime(start_date, '%Y-%m-%d')
        except ValueError:
            try:
                date_obj = datetime.datetime.strptime(start_date, '%Y-%m-%d')
            except ValueError:
                raise ValueError(f"无效日期格式：{start_date}。请使用YYYY-MM-DD或YYYY-M-D")

        cache_key = date_obj.strftime('%Y-%m-%d')

        # 缓存检查
        if cache_key in self.schedule_cache:
            return self.schedule_cache[cache_key]

        # 计算关键路径
        try:
            self.create_project_network()
            self.project_network.update_all()
        except Exception as e:
            raise RuntimeError(f"关键路径计算失败：{str(e)}")

        # 设置活动时间参数
        for activity in self.project_network.nodes:
            activity.skd_erect_start_date = activity.es
            activity.skd_erect_finish_date = activity.es + activity.duration
            activity.act_erect_start_date = activity.skd_erect_start_date
            activity.act_erect_finish_date = activity.skd_erect_finish_date
            activity.delivery_date = activity.skd_erect_start_date

        # 缓存结果
        result = (list(self.project_network.nodes), date_obj)
        self.schedule_cache[cache_key] = result
        return result

    def update_successors(self, modified_activity):
        """级联更新后续活动时间"""
        original_finish = modified_activity.act_erect_finish_date

        modified_activity.act_erect_start_date = modified_activity.es
        modified_activity.act_erect_finish_date = (
            modified_activity.act_erect_start_date
            + modified_activity.act_duration
        )

        if modified_activity.act_erect_finish_date <= original_finish:
            return

        for successor in modified_activity.to_nodes:
            if modified_activity.act_erect_finish_date > successor.es:
                successor.es = modified_activity.act_erect_finish_date
            self.update_successors(successor)


if __name__ == '__main__':
    def main():
        """测试用例"""
        scheduler = ProjectScheduler()

        # 测试不同日期格式
        test_dates = ['2023-03-10', '2023-3-10']
        for date_str in test_dates:
            try:
                activities, start_date = scheduler.get_schedule(date_str)
                print(f"\n测试日期：{date_str}")
                print(f"开始日期：{start_date.strftime('%Y-%m-%d')}")
                print(f"总活动数：{len(activities)}")
                print("前5个活动：")
                for act in activities[:5]:
                    print(f"{act.name}: {act.duration}天")
            except Exception as e:
                print(f"错误：{str(e)}")

    main()